/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cg_entity.cpp - entity managment recived from the server-game
//


#include "cg_local.h"


/*
 ==============================================================================

 ACTIVE ENTITY LIST

 ==============================================================================
*/


/*
 ==================
 CG_SetEntitySort
 ==================
*/
static void CG_SetEntitySort (entity_t *entity){

	entity_t	*master;
	int			depth;

	// Make sure players always come first
	if (entity->state->type == ET_PLAYER){
		entity->sort = 0;
		return;
	}

	depth = 1;

	// If bound to another entity
	if (entity->state->bindMaster != ENTITYNUM_NONE){
		master = entity;

		while (master->state->bindMaster != ENTITYNUM_NONE){
			master = &cgs.entities[master->state->bindMaster];

			depth++;
		}
	}

	entity->sort = depth;
}

/*
 ==================
 CG_SortActiveEntityList
 ==================
*/
static int CG_SortActiveEntityList (const void *elem1, const void *elem2){

	const entity_t	*entity1 = *(const entity_t **)elem1;
	const entity_t	*entity2 = *(const entity_t **)elem2;

	return entity1->sort - entity2->sort;
}

/*
 ==================
 CG_BuildActiveEntityList
 ==================
*/
void CG_BuildActiveEntityList (){

	entity_t		*entity;
	entityState_t	*state;
	int				i;

	// Build the active entity list
	cg.numActiveEntities = 0;

	for (i = 0; i < cg.snapshot->numEntities; i++){
		state = &cg.snapshot->entityStates[i];
		entity = &cgs.entities[state->number];

		// Set the sort value
		CG_SetEntitySort(entity);

		// Add it to the active entity list
		cg.activeEntities[cg.numActiveEntities++] = entity;
	}

	// Sort the active entity list so that masters come before slaves and
	// players always come first
	qsort(cg.activeEntities, cg.numActiveEntities, sizeof(entity_t *), CG_SortActiveEntityList);
}


/*
 ==============================================================================

 ENTITY SETUP

 ==============================================================================
*/


/*
 ==================
 CG_LerpPositionAndOrientation

 This will linearize a sine or parabolic curve, but it is important to not
 extrapolate positions and orientations for certain entities if more recent
 data is available
 ==================
*/
static void CG_LerpPositionAndOrientation (entity_t *entity){

	glqVec3		origin, nextOrigin;
	glqAngles	angles, nextAngles;

	origin = entity->state->origin.Evaluate(cg.snapshot->time, cgs.gravity);
	angles = entity->state->angles.Evaluate(cg.snapshot->time, cgs.gravity);

	nextOrigin = entity->nextState->origin.Evaluate(cg.nextSnapshot->time, cgs.gravity);
	nextAngles = entity->nextState->angles.Evaluate(cg.nextSnapshot->time, cgs.gravity);

	// Interpolate
	entity->lerpOrigin.LerpFast(origin, nextOrigin, cg.frameInterpolation);
	entity->lerpAngles.LerpFast(angles, nextAngles, cg.frameInterpolation);
}

/*
 ==================
 CG_CalcPositionAndOrientation
 ==================
*/
static void CG_CalcPositionAndOrientation (entity_t *entity){

	glqVec3	baseOrigin, masterOrigin;
	glqMat3	baseAxis, masterAxis;

	// Calculate position and orientation
	if (entity->interpolate && entity->state->origin.type == TR_INTERPOLATE)
		CG_LerpPositionAndOrientation(entity);
	else {
		// First see if we can interpolate between two snapshots for linear
		// extrapolated clients
		if (entity->interpolate && entity->state->origin.type == TR_LINEAR_STOP && entity->state->number < MAX_CLIENTS)
			CG_LerpPositionAndOrientation(entity);
		else {
			// Just use the current snapshot and evaluate as best we can
			if (entity->state->flags & EF_BLOCKED){
				entity->lerpOrigin = entity->state->origin.Evaluate(cg.snapshot->time, cgs.gravity);
				entity->lerpAngles = entity->state->angles.Evaluate(cg.snapshot->time, cgs.gravity);
			}
			else {
				entity->lerpOrigin = entity->state->origin.Evaluate(cg.time, cgs.gravity);
				entity->lerpAngles = entity->state->angles.Evaluate(cg.time, cgs.gravity);
			}
		}
	}

	entity->lerpAngles.Normalize360();
	entity->lerpAxis = entity->lerpAngles.ToMat3();
}

/*
 ==================
 
 ==================
*/
template <class T> static void CG_SetupClipping (entity_t *entity, T *state){

}

/*
 ==================
 CG_SetupEntities
 ==================
*/
void CG_SetupEntities (){

	entity_t	*entity;
	int			i;

	// Go through all the active entities
	for (i = 0; i < cg.numActiveEntities; i++){
		entity = cg.activeEntities[i];

		// Save the last position and orientation
		entity->lastLerpOrigin = entity->lerpOrigin;
		entity->lastLerpAngles = entity->lerpAngles;
		entity->lastLerpAxis = entity->lerpAxis;

		// Calculate position and orientation
		CG_CalcPositionAndOrientation(entity);

		// If it wasn't present in the last frame
		if (!entity->lastValid){
			entity->lastValid = true;

			// Copy the current position and orientation
			entity->lastLerpOrigin = entity->lerpOrigin;
			entity->lastLerpAngles = entity->lerpAngles;
			entity->lastLerpAxis = entity->lerpAxis;
		}

		// If it should be ignored for anything other than binding
		if (entity->state->flags & EF_IGNORE)
			continue;

		// Set up clipping
		switch (entity->state->type){
		case ET_GENERAL:
			CG_SetupClipping(entity, (general_t *)entity->state->buffer);
			break;
		case ET_MOVER:
			CG_SetupClipping(entity, (mover_t *)entity->state->buffer);
			break;
		}
	}
}


/*
 ==============================================================================

 ENTITY UPDATES

 ==============================================================================
*/


/*
 ==================
 CG_CalcVelocity
 ==================
*/
static glqVec3 CG_CalcVelocity (entity_t *entity){

	glqVec3	dir;
	float	len;

	dir = entity->lerpOrigin - entity->lastLerpOrigin;

	len = dir.Normalize();

	return dir * (len / cg.frameTime);
}

/*
 ==================
 
 ==================
*/
static void CG_UpdateRenderEntity (entity_t *entity){

	// If not visible
	if (!(entity->state->flags & EF_VISIBLE)){
		// Remove the render entity
		if (entity->entityHandle){
			glqRenderer->RemoveRenderEntity(cgs.scene, entity->entityHandle);
			entity->entityHandle = 0;
		}

		return;
	}

	// Set up the render entity
	entity->renderEntity.entityNum = entity->state->number;

	entity->renderEntity.allowInView = entity->state->flags;
	entity->renderEntity.allowShadowInView = entity->state->flags;

	// TODO: referenceLight/soundEmitterHandle

	// Add or update the render entity
	if (!entity->entityHandle)
		entity->entityHandle = glqRenderer->AddRenderEntity(cgs.scene, &entity->renderEntity);
	else
		glqRenderer->UpdateRenderEntity(cgs.scene, entity->entityHandle, &entity->renderEntity);
}

/*
 ==================
 
 ==================
*/
static void CG_UpdateRenderLight (entity_t *entity){

	// If not visible
	if (!(entity->state->flags & EF_VISIBLE)){
		// Remove the render light
		if (entity->lightHandle){
			glqRenderer->RemoveRenderLight(cgs.scene, entity->lightHandle);
			entity->lightHandle = 0;
		}

		return;
	}

	// Set up the render light
	entity->renderLight.lightNum = entity->state->number;

	entity->renderLight.allowInView = entity->state->flags;

	// TODO: referenceSound

	// Add or update the render light
	if (!entity->lightHandle)
		entity->lightHandle = glqRenderer->AddRenderLight(cgs.scene, &entity->renderLight);
	else
		glqRenderer->UpdateRenderLight(cgs.scene, entity->lightHandle, &entity->renderLight);
}

/*
 ==================
 CG_UpdateSoundEmitter
 ==================
*/
static void CG_UpdateSoundEmitter (entity_t *entity, int soundTime, soundShader_t *soundShader){

	// If not audible
	if (!(entity->state->flags & EF_AUDIBLE)){
		// Clear the automatic looping sound
		entity->loopSound = NULL;

		// Remove the sound emitter
		if (entity->emitterHandle){
			glqSound->RemoveSoundEmitter(entity->emitterHandle, true);
			entity->emitterHandle = 0;
		}

		return;
	}

	// Set up the sound emitter
	entity->soundEmitter.emitterId = entity->state->number;

	if (CG_Contents(entity->soundEmitter.origin, bounds_zero, MASK_LIQUID, true, ENTITYNUM_NONE) & MASK_LIQUID)
		entity->soundEmitter.underwater = true;
	else
		entity->soundEmitter.underwater = false;

	entity->soundEmitter.velocity = CG_CalcVelocity(entity);

	// Add or update the sound emitter
	if (!entity->emitterHandle)
		entity->emitterHandle = glqSound->AddSoundEmitter(&entity->soundEmitter);
	else
		glqSound->UpdateSoundEmitter(entity->emitterHandle, &entity->soundEmitter);

	// Play and stop automatic looping sounds as needed
	if (!entity->emitterHandle || entity->loopSound == soundShader)
		return;

	if (entity->loopSound)
		glqSound->StopSoundFromEmitter(entity->emitterHandle, SND_CHANNEL_AMBIENT);

	entity->loopSound = soundShader;

	if (entity->loopSound)
		glqSound->PlaySoundFromEmitter(entity->emitterHandle, SND_CHANNEL_AMBIENT, soundTime, soundShader);
}

/*
 ==================
 CG_UpdateEntity_General
 ==================
*/
static void CG_UpdateEntity_General (entity_t *entity){

	general_t	*state, *nextState;

	state = (general_t *)entity->state->buffer;

	// If visible
	if (entity->state->flags & EF_VISIBLE){
		// Set up the render entity
		entity->renderEntity.type = RE_MODEL;
		entity->renderEntity.origin = entity->lerpOrigin;
		entity->renderEntity.axis = entity->lerpAxis;
		entity->renderEntity.model = cgs.gameModels[state->model];
		entity->renderEntity.hasSubview = false;
		entity->renderEntity.depthHack = false;
		entity->renderEntity.material = cgs.gameMaterials[state->material];
		entity->renderEntity.materialParms[0] = state->materialParms[0];
		entity->renderEntity.materialParms[1] = state->materialParms[1];
		entity->renderEntity.materialParms[2] = state->materialParms[2];
		entity->renderEntity.materialParms[3] = state->materialParms[3];
		entity->renderEntity.materialParms[4] = state->materialParms[4];
		entity->renderEntity.materialParms[5] = state->materialParms[5];
		entity->renderEntity.materialParms[6] = state->materialParms[6];
		entity->renderEntity.materialParms[7] = state->materialParms[7];

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){
			nextState = (general_t *)entity->nextState->buffer;

			entity->renderEntity.materialParms[0] = glqMath::LerpFast(state->materialParms[0], nextState->materialParms[0], cg.frameInterpolation);
			entity->renderEntity.materialParms[1] = glqMath::LerpFast(state->materialParms[1], nextState->materialParms[1], cg.frameInterpolation);
			entity->renderEntity.materialParms[2] = glqMath::LerpFast(state->materialParms[2], nextState->materialParms[2], cg.frameInterpolation);
			entity->renderEntity.materialParms[3] = glqMath::LerpFast(state->materialParms[3], nextState->materialParms[3], cg.frameInterpolation);
		}
	}

	// If audible
	if (entity->state->flags & EF_AUDIBLE){
		// Set up the sound emitter
		entity->soundEmitter.origin = entity->absBounds.GetCenter();
		entity->soundEmitter.direction.Clear();
		entity->soundEmitter.soundParms[0] = state->soundParms[0];
		entity->soundEmitter.soundParms[1] = state->soundParms[1];
		entity->soundEmitter.soundParms[2] = state->soundParms[2];
		entity->soundEmitter.soundParms[3] = state->soundParms[3];
		entity->soundEmitter.soundParms[4] = state->soundParms[4];
		entity->soundEmitter.soundParms[5] = state->soundParms[5];
		entity->soundEmitter.soundParms[6] = state->soundParms[6];
		entity->soundEmitter.soundParms[7] = state->soundParms[7];

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){
			nextState = (general_t *)entity->nextState->buffer;

			entity->soundEmitter.soundParms[0] = glqMath::LerpFast(state->soundParms[0], nextState->soundParms[0], cg.frameInterpolation);
			entity->soundEmitter.soundParms[1] = glqMath::LerpFast(state->soundParms[1], nextState->soundParms[1], cg.frameInterpolation);
			entity->soundEmitter.soundParms[2] = glqMath::LerpFast(state->soundParms[2], nextState->soundParms[2], cg.frameInterpolation);
			entity->soundEmitter.soundParms[3] = glqMath::LerpFast(state->soundParms[3], nextState->soundParms[3], cg.frameInterpolation);
			entity->soundEmitter.soundParms[4] = glqMath::LerpFast(state->soundParms[4], nextState->soundParms[4], cg.frameInterpolation);
			entity->soundEmitter.soundParms[5] = glqMath::LerpFast(state->soundParms[5], nextState->soundParms[5], cg.frameInterpolation);
		}
	}

	// Update the render entity
	CG_UpdateRenderEntity(entity);

	// Update the sound emitter
	CG_UpdateSoundEmitter(entity, state->soundTime, cgs.gameSoundShaders[state->sound]);
}

/*
 ==================
 
 ==================
*/
static void CG_UpdateEntity_Mover (entity_t *entity){

	mover_t	*state, *nextState;

	state = (mover_t *)entity->state->buffer;

	// If visible
	if (entity->state->flags & EF_VISIBLE){
		// Set up the render entity
		entity->renderEntity.type = RE_MODEL;
		entity->renderEntity.origin = entity->lerpOrigin;
		entity->renderEntity.axis = entity->lerpAxis;
		entity->renderEntity.model = cgs.gameModels[state->model];
		entity->renderEntity.hasSubview = false;
		entity->renderEntity.depthHack = false;
		entity->renderEntity.material = NULL;
		entity->renderEntity.materialParms[0] = 1.0f;
		entity->renderEntity.materialParms[1] = 1.0f;
		entity->renderEntity.materialParms[2] = 1.0f;
		entity->renderEntity.materialParms[3] = 1.0f;
		entity->renderEntity.materialParms[4] = 0.0f;
		entity->renderEntity.materialParms[5] = 0.0f;
		entity->renderEntity.materialParms[6] = 0.0f;
		entity->renderEntity.materialParms[7] = 0.0f;

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){
			nextState = (mover_t *)entity->nextState->buffer;

		}
	}

	// If audible
	if (entity->state->flags & EF_AUDIBLE){

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){
			nextState = (mover_t *)entity->nextState->buffer;

		}
	}

	// Update the render entity
	CG_UpdateRenderEntity(entity);

	// Update the sound emitter
}

/*
 ==================
 
 ==================
*/
static void CG_UpdateEntity_Beam (entity_t *entity){

}

/*
 ==================
 CG_UpdateEntity_Light
 ==================
*/
static void CG_UpdateEntity_Light (entity_t *entity){

	light_t	*state, *nextState;

	state = (light_t *)entity->state->buffer;

	// If visible
	if (entity->state->flags & EF_VISIBLE){
		// Set up the render light
		entity->renderLight.type = (rlType_t)state->type;
		entity->renderLight.origin = entity->lerpOrigin;
		entity->renderLight.center = state->center;
		entity->renderLight.axis = entity->lerpAxis;
		entity->renderLight.radius = state->radius;
		entity->renderLight.xMin = state->xMin;
		entity->renderLight.xMax = state->xMax;
		entity->renderLight.yMin = state->yMin;
		entity->renderLight.yMax = state->yMax;
		entity->renderLight.zNear = state->zNear;
		entity->renderLight.zFar = state->zFar;
		entity->renderLight.noShadows = state->noShadows;
		entity->renderLight.shadowBias = state->shadowBias;
		entity->renderLight.shadowSlopeScaleBias = state->shadowSlopeScaleBias;
		entity->renderLight.shadowSoftness = state->shadowSoftness;
		entity->renderLight.fogDistance = state->fogDistance;
		entity->renderLight.fogHeight = state->fogHeight;
		entity->renderLight.volumeIntensity = state->volumeIntensity;
		entity->renderLight.volumeShadows = state->volumeShadows;
		entity->renderLight.volumeInteractions = state->volumeInteractions;
		entity->renderLight.detailLevel = state->detailLevel;
		entity->renderLight.material = cgs.gameMaterials[state->material];
		entity->renderLight.materialParms[0] = state->materialParms[0];
		entity->renderLight.materialParms[1] = state->materialParms[1];
		entity->renderLight.materialParms[2] = state->materialParms[2];
		entity->renderLight.materialParms[3] = state->materialParms[3];
		entity->renderLight.materialParms[4] = state->materialParms[4];
		entity->renderLight.materialParms[5] = state->materialParms[5];
		entity->renderLight.materialParms[6] = state->materialParms[6];
		entity->renderLight.materialParms[7] = state->materialParms[7];

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){
			nextState = (light_t *)entity->nextState->buffer;

			entity->renderLight.materialParms[0] = glqMath::LerpFast(state->materialParms[0], nextState->materialParms[0], cg.frameInterpolation);
			entity->renderLight.materialParms[1] = glqMath::LerpFast(state->materialParms[1], nextState->materialParms[1], cg.frameInterpolation);
			entity->renderLight.materialParms[2] = glqMath::LerpFast(state->materialParms[2], nextState->materialParms[2], cg.frameInterpolation);
			entity->renderLight.materialParms[3] = glqMath::LerpFast(state->materialParms[3], nextState->materialParms[3], cg.frameInterpolation);
		}
	}

	// Update the render light
	CG_UpdateRenderLight(entity);
}

/*
 ==================
 CG_UpdateEntity_Speaker
 ==================
*/
static void CG_UpdateEntity_Speaker (entity_t *entity){

	speaker_t	*state, *nextState;

	state = (speaker_t *)entity->state->buffer;

	// If audible
	if (entity->state->flags & EF_AUDIBLE){
		// Set up the sound emitter
		entity->soundEmitter.origin = entity->lerpOrigin;
		entity->soundEmitter.direction = state->direction;
		entity->soundEmitter.soundParms[0] = state->soundParms[0];
		entity->soundEmitter.soundParms[1] = state->soundParms[1];
		entity->soundEmitter.soundParms[2] = state->soundParms[2];
		entity->soundEmitter.soundParms[3] = state->soundParms[3];
		entity->soundEmitter.soundParms[4] = state->soundParms[4];
		entity->soundEmitter.soundParms[5] = state->soundParms[5];
		entity->soundEmitter.soundParms[6] = state->soundParms[6];
		entity->soundEmitter.soundParms[7] = state->soundParms[7];

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){
			nextState = (speaker_t *)entity->nextState->buffer;

			entity->soundEmitter.direction.LerpFast(state->direction, nextState->direction, cg.frameInterpolation);
			entity->soundEmitter.direction.Normalize();

			entity->soundEmitter.soundParms[0] = glqMath::LerpFast(state->soundParms[0], nextState->soundParms[0], cg.frameInterpolation);
			entity->soundEmitter.soundParms[1] = glqMath::LerpFast(state->soundParms[1], nextState->soundParms[1], cg.frameInterpolation);
			entity->soundEmitter.soundParms[2] = glqMath::LerpFast(state->soundParms[2], nextState->soundParms[2], cg.frameInterpolation);
			entity->soundEmitter.soundParms[3] = glqMath::LerpFast(state->soundParms[3], nextState->soundParms[3], cg.frameInterpolation);
			entity->soundEmitter.soundParms[4] = glqMath::LerpFast(state->soundParms[4], nextState->soundParms[4], cg.frameInterpolation);
			entity->soundEmitter.soundParms[5] = glqMath::LerpFast(state->soundParms[5], nextState->soundParms[5], cg.frameInterpolation);
		}
	}

	// Update the sound emitter
	CG_UpdateSoundEmitter(entity, state->soundTime, cgs.gameSoundShaders[state->sound]);
}

/*
 ==================

 NOTE: will be used for other clients?
 ==================
*/
static void CG_UpdateEntity_Player (entity_t *entity){

	player_t	*state, *nextState;

	state = (player_t *)entity->state->buffer;

	// If visible
	if (entity->state->flags & EF_VISIBLE){
		// Set up the render entity
		entity->renderEntity.type = RE_MODEL;
		entity->renderEntity.origin = entity->lerpOrigin;
		entity->renderEntity.axis = entity->lerpAxis;
		entity->renderEntity.model = cgs.gameModels[state->model];
		entity->renderEntity.hasSubview = false;
		entity->renderEntity.depthHack = false;

		entity->renderEntity.materialParms[0] = 1.0f;
		entity->renderEntity.materialParms[1] = 1.0f;
		entity->renderEntity.materialParms[2] = 1.0f;
		entity->renderEntity.materialParms[3] = 1.0f;
		entity->renderEntity.materialParms[4] = 0.0f;
		entity->renderEntity.materialParms[5] = 0.0f;
		entity->renderEntity.materialParms[6] = 0.0f;
		entity->renderEntity.materialParms[7] = 0.0f;

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){

		}
	}

	// If audible
	if (entity->state->flags & EF_AUDIBLE){

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){

		}
	}

	// Update the render entity
	CG_UpdateRenderEntity(entity);

	// Update the sound emitter
	CG_UpdateSoundEmitter(entity, state->soundTime, cgs.gameSoundShaders[state->sound]);
}

// FIXME: move this into glqAngle?
float AngleMod (float angle){

	return (360.0f / 65536) * ((int)(angle * (65536 / 360.0f)) & 65535);
}

/*
 ==================
 
 TODO: some items like health, ammo boxes etc. do not rotate so we need to make some checks later
 ==================
*/
static void CG_UpdateEntity_Item (entity_t *entity){

	item_t		*state, *nextState;
	glqAngles	autoRotate;
	glqMat3		autoRotateAxis;
	float		scale;

	state = (item_t *)entity->state->buffer;

	// If visible
	if (entity->state->flags & EF_VISIBLE){
		// Set up items that bob up and down continuously
		scale = 0.005f + entity->state->number * 0.00001;
		entity->lerpOrigin[2] += 4.0f + glqMath::Cos((cg.time + 1000) * scale) * 4.0f;

		// Set up items that auto-rotate
		autoRotate.Set(0.0f, AngleMod(cg.time * 0.1f), 0.0f);
		autoRotateAxis = autoRotate.ToMat3();

		// Set up respawned items scaling

		// Set up the render entity
		entity->renderEntity.type = RE_MODEL;
		entity->renderEntity.origin = entity->lerpOrigin;
		entity->renderEntity.axis = entity->lerpAxis.Multiply(autoRotateAxis);
		entity->renderEntity.model = cgs.gameModels[state->model];
		entity->renderEntity.hasSubview = false;
		entity->renderEntity.depthHack = false;

		entity->renderEntity.materialParms[0] = 1.0f;
		entity->renderEntity.materialParms[1] = 1.0f;
		entity->renderEntity.materialParms[2] = 1.0f;
		entity->renderEntity.materialParms[3] = 1.0f;
		entity->renderEntity.materialParms[4] = 0.0f;
		entity->renderEntity.materialParms[5] = 0.0f;
		entity->renderEntity.materialParms[6] = 0.0f;
		entity->renderEntity.materialParms[7] = 0.0f;

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){

		}
	}

	// If audible
	if (entity->state->flags & EF_AUDIBLE){

	}

	// Update the render entity
	CG_UpdateRenderEntity(entity);

	// Update the sound emitter
	CG_UpdateSoundEmitter(entity, state->soundTime, cgs.gameSoundShaders[state->sound]);
}

/*
 ==================
 
 ==================
*/
static void CG_UpdateEntity_Monster (entity_t *entity){

	monster_t	*state, *nextState;

	state = (monster_t *)entity->state->buffer;

	// If visible
	if (entity->state->flags & EF_VISIBLE){
		// Set up the render entity
		entity->renderEntity.type = RE_MODEL;
		entity->renderEntity.origin = entity->lerpOrigin;
		entity->renderEntity.axis = entity->lerpAxis;
		entity->renderEntity.model = cgs.gameModels[state->model];
		entity->renderEntity.frame = entity->state->frame;
		entity->renderEntity.oldFrame = entity->state->frame;
		entity->renderEntity.backLerp = 1.0f - cg.frameInterpolation;
		entity->renderEntity.hasSubview = false;
		entity->renderEntity.depthHack = false;

		entity->renderEntity.materialParms[0] = 1.0f;
		entity->renderEntity.materialParms[1] = 1.0f;
		entity->renderEntity.materialParms[2] = 1.0f;
		entity->renderEntity.materialParms[3] = 1.0f;
		entity->renderEntity.materialParms[4] = 0.0f;
		entity->renderEntity.materialParms[5] = 0.0f;
		entity->renderEntity.materialParms[6] = 0.0f;
		entity->renderEntity.materialParms[7] = 0.0f;

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){
			nextState = (monster_t *)entity->nextState->buffer;

			entity->renderEntity.frame = entity->nextState->frame;
		}
	}

	// If audible
	if (entity->state->flags & EF_AUDIBLE){

		// Interpolate if possible
		if (entity->interpolate && cg.frameInterpolation){

		}
	}

	// Update the render entity
	CG_UpdateRenderEntity(entity);

	// Update the sound emitter
	CG_UpdateSoundEmitter(entity, state->soundTime, cgs.gameSoundShaders[state->sound]);
}

/*
 ==================
 
 ==================
*/
static void CG_UpdateEntity_Projectile (entity_t *entity){

	projectile_t	*state, *nextState;

	state = (projectile_t *)entity->state->buffer;

	// If visible
	if (entity->state->flags & EF_VISIBLE){

	}

	// If audible
	if (entity->state->flags & EF_AUDIBLE){

	}

	// Update the render entity
	CG_UpdateRenderEntity(entity);

	// Update the sound emitter
}

/*
 ==================
 CG_UpdateEntities
 ==================
*/
void CG_UpdateEntities (){

	entity_t	*entity;
	int			i;

	// Go through all the active entities
	for (i = 0; i < cg.numActiveEntities; i++){
		entity = cg.activeEntities[i];

		// If it should be ignored for anything other than binding
		if (entity->state->flags & EF_IGNORE)
			continue;

		// Update renderer and sound system data
		switch (entity->state->type){
		case ET_GENERAL:
			CG_UpdateEntity_General(entity);
			break;
		case ET_MOVER:
			CG_UpdateEntity_Mover(entity);
			break;
		case ET_BEAM:
			CG_UpdateEntity_Beam(entity);
			break;
		case ET_LIGHT:
			CG_UpdateEntity_Light(entity);
			break;
		case ET_SPEAKER:
			CG_UpdateEntity_Speaker(entity);
			break;
		case ET_PLAYER:
			CG_UpdateEntity_Player(entity);
			break;
		case ET_ITEM:
			CG_UpdateEntity_Item(entity);
			break;
		case ET_MONSTER:
			CG_UpdateEntity_Monster(entity);
			break;
		case ET_PROJECTILE:
			CG_UpdateEntity_Projectile(entity);
			break;
		default:
			G_Error("CG_UpdateEntities: bad entity type (%i)", entity->state->type);		
		}
	}
}

/*
 ==================
 
 ==================
*/
void CG_UpdatePlayerEntity (){

	// Generate the entity state from the player state
	cg.predictedPlayerEntity.state = &cg.predictedPlayerEntityState;

	G_PlayerStateToEntityState(&cg.predictedPlayerState, &cg.predictedPlayerEntityState);

	// Calculate position and orientation
	CG_CalcPositionAndOrientation(&cg.predictedPlayerEntity);

	// TODO: we need to update the renderEntity and sound here for the local client, because 
	// ET_PLAYER is used for other connected clients
}


/*
 ==============================================================================

 ENTITY CLEARING

 ==============================================================================
*/


/*
 ==================
 CG_ClearEntities
 ==================
*/
void CG_ClearEntities (){

	entity_t	*entity;
	int			i;

	// Go through all the entities
	for (i = 0; i < MAX_ENTITIES; i++){
		entity = &cgs.entities[i];

		// Clear the automatic looping sound
		entity->loopSound = NULL;

		// Free all renderer and sound system handles
		if (entity->entityHandle){
			glqRenderer->RemoveRenderEntity(cgs.scene, entity->entityHandle);
			entity->entityHandle = 0;
		}
		if (entity->lightHandle){
			glqRenderer->RemoveRenderLight(cgs.scene, entity->lightHandle);
			entity->lightHandle = 0;
		}
		if (entity->emitterHandle){
			glqSound->RemoveSoundEmitter(entity->emitterHandle, true);
			entity->emitterHandle = 0;
		}
	}
}

/*
 ==================
 CG_ClearPlayerEntity
 ==================
*/
void CG_ClearPlayerEntity (){

	// Clear the automatic looping sound
	cg.predictedPlayerEntity.loopSound = NULL;

	// Free all renderer and sound system handles
	if (cg.predictedPlayerEntity.entityHandle){
		glqRenderer->RemoveRenderEntity(cgs.scene, cg.predictedPlayerEntity.entityHandle);
		cg.predictedPlayerEntity.entityHandle = 0;
	}
	if (cg.predictedPlayerEntity.lightHandle){
		glqRenderer->RemoveRenderLight(cgs.scene, cg.predictedPlayerEntity.lightHandle);
		cg.predictedPlayerEntity.lightHandle = 0;
	}
	if (cg.predictedPlayerEntity.emitterHandle){
		glqSound->RemoveSoundEmitter(cg.predictedPlayerEntity.emitterHandle, true);
		cg.predictedPlayerEntity.emitterHandle = 0;
	}
}